package cz.muni.fi.pa165.web;

import cz.muni.fi.pa165.api.ForestService;
import cz.muni.fi.pa165.api.ForestVisitService;
import cz.muni.fi.pa165.api.MushroomService;
import cz.muni.fi.pa165.api.PickerService;
import cz.muni.fi.pa165.api.dto.ForestDTO;
import cz.muni.fi.pa165.api.dto.ForestVisitDTO;
import cz.muni.fi.pa165.api.dto.MushroomDTO;
import cz.muni.fi.pa165.api.dto.PickerDTO;
import cz.muni.fi.pa165.web.validation.ForestVisitDTOValidator;
import java.beans.PropertyEditorSupport;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller class for working with ForestVisit
 *
 * @author Matej Cimbora
 */
@Controller
public class ForestVisitController {

    @Autowired
    private ForestVisitService forestVisitService;
    @Autowired
    private ForestService forestService;
    @Autowired
    private PickerService pickerService;
    @Autowired
    private MushroomService mushroomService;
    @Autowired
    private ForestVisitDTOValidator forestVisitValidator;

    @RequestMapping(value = "auth/forestVisit")
    public String init(ModelMap modelMap) {
        ForestVisitDTO forestVisit = new ForestVisitDTO();
        modelMap.addAttribute("command", forestVisit);
        modelMap.addAttribute("visits", forestVisitService.retrieveAllVisits());
        return "auth/forestVisit";
    }

    @RequestMapping(value = "auth/addForestVisit")
    public String addVisit(@ModelAttribute("command") ForestVisitDTO forestVisit,
            BindingResult result, ModelMap modelMap) {
        forestVisitValidator.validate(forestVisit, result);
        if (result.hasErrors()) {
            modelMap.addAttribute("command", forestVisit);
            modelMap.addAttribute("visits", forestVisitService.retrieveAllVisits());
            return "auth/forestVisit";
        } else {
            forestVisitService.createVisit(forestVisit);
            modelMap.addAttribute("command", forestVisit);
            modelMap.addAttribute("visits", forestVisitService.retrieveAllVisits());
            return "auth/forestVisit";
        }
    }

    @RequestMapping(value = "auth/editForestVisit*")
    public String editForestVisit(@RequestParam("id") String request, ModelMap model) {
        ForestVisitDTO forestVisit = forestVisitService.getVisit(Long.valueOf(request));
        model.addAttribute("command", forestVisit);
        return "auth/editForestVisit";
    }

    @RequestMapping(value = "auth/editedForestVisit")
    public String editedForestVisit(@ModelAttribute("command") ForestVisitDTO forestVisit,
            BindingResult result, ModelMap modelMap) {
        forestVisitValidator.validate(forestVisit, result);
        if (result.hasErrors()) {
            modelMap.addAttribute("command", forestVisit);
            return "auth/editForestVisit";
        } else {
            forestVisitService.updateVisit(forestVisit);
            modelMap.addAttribute("command", new ForestVisitDTO());
            modelMap.addAttribute("visits", forestVisitService.retrieveAllVisits());
            return "auth/forestVisit";
        }
    }

    @RequestMapping(value = "auth/removeForestVisit*")
    public String removeForestVisit(@RequestParam("id") String request, ModelMap model) {
        ForestVisitDTO forestVisit = forestVisitService.getVisit(Long.valueOf(request));
        forestVisitService.removeVisit(forestVisit);
        model.addAttribute("visits", forestVisitService.retrieveAllVisits());
        model.addAttribute("command", new ForestVisitDTO());
        return "auth/forestVisit";
    }

    @RequestMapping(value = "auth/showMushrooms*")
    public String showMushrooms(@RequestParam("id") String request, ModelMap model) {
        ForestVisitDTO fv = forestVisitService.getVisit(Long.valueOf(request));
        model.addAttribute("mushrooms", fv.getMushroomsFound());
        model.addAttribute("command", fv);
        return "auth/forestVisitMushrooms";
    }

    @RequestMapping(value = "auth/visitSort*")
    public String sortVisits(@RequestParam("name") String name, @RequestParam("sortBy") String sortBy, ModelMap model) {
        if (name.isEmpty() || sortBy.isEmpty()) {
            model.addAttribute("visits", forestVisitService.retrieveAllVisits());
        }
        try {
            switch (sortBy) {
                case "byPicker":
                    PickerDTO p = pickerService.findPickerByNickName(name);
                    model.addAttribute("visits", forestVisitService.retrieveVisitsByPicker(p));
                    break;
                case "byForest":
                    ForestDTO f = forestService.retrieveForestByName(name);
                    model.addAttribute("visits", forestVisitService.retrieveVisitsByForest(f));
                    break;
                case "default":
                    model.addAttribute("visits", forestVisitService.retrieveAllVisits());
            }
        } catch (Exception ex) {
            model.addAttribute("visits", forestVisitService.retrieveAllVisits());
        }
        model.addAttribute("command", new ForestVisitDTO());
        return "auth/forestVisit";
    }

    @ModelAttribute("forests")
    public List<ForestDTO> getForests() {
        List<ForestDTO> list = new ArrayList<>();
        for (ForestDTO forest : forestService.retrieveAllForests()) {
            list.add(forest);
        }
        return list;
    }

    @ModelAttribute("pickers")
    public List<PickerDTO> getPickers() {
        List<PickerDTO> list = new ArrayList<>();
        for (PickerDTO picker : pickerService.findAllPickers()) {
            list.add(picker);
        }
        return list;
    }

    @ModelAttribute("mushrooms")
    public List<MushroomDTO> getMushrooms() {
        List<MushroomDTO> list = new ArrayList<>();
        for (MushroomDTO mushroom : mushroomService.findAllMushrooms()) {
            list.add(mushroom);
        }
        return list;
    }

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
        CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
        binder.registerCustomEditor(Date.class, editor);
        binder.registerCustomEditor(PickerDTO.class, new PickerEditor());
        binder.registerCustomEditor(ForestDTO.class, new ForestEditor());
        binder.registerCustomEditor(List.class, "mushroomsFound", new CustomCollectionEditor(List.class) {
            @Override
            protected Object convertElement(Object element) {
                MushroomDTO mushroom = null;
                if (element instanceof String && !((String) element).equals("")) {
                    mushroom = mushroomService.findMushroomsByName((String) element);
                }
                return mushroom;
            }
        });
    }

    private class PickerEditor extends PropertyEditorSupport {

        PickerDTO picker;

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text != null && !text.isEmpty()) {
                picker = pickerService.findPickerByNickName(text);
            }
            setValue(picker);
        }

        @Override
        public String getAsText() {
            if (picker == null) {
                return null;
            } else {
                return picker.toString();
            }
        }
    }

    private class ForestEditor extends PropertyEditorSupport {

        ForestDTO forest;

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text != null && !text.isEmpty()) {
                forest = forestService.retrieveForestByName(text);
            }
            setValue(forest);
        }

        @Override
        public String getAsText() {
            if (forest == null) {
                return null;
            } else {
                return forest.toString();
            }
        }
    }

    private class MushroomEditor extends PropertyEditorSupport {

        ForestDTO forest;

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text != null && !text.isEmpty()) {
                forest = forestService.retrieveForestByName(text);
            }
            setValue(forest);
        }

        @Override
        public String getAsText() {
            if (forest == null) {
                return null;
            } else {
                return forest.toString();
            }
        }
    }

    @ExceptionHandler(DataAccessException.class)
    public ModelAndView handleException(DataAccessException ex) {

        ModelAndView m = new ModelAndView();
        m.setViewName("forestVisit");
        m.addObject("command", new ForestVisitDTO());
        m.addObject("visits", forestVisitService.retrieveAllVisits());
        m.addObject("error", "Error has occured, try again");
        return m;
    }
}
